﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace CollisionDetectionSandbox
{
    class GameScreen : Screen
    {
        public static Texture2D FireTexture;
        public static Texture2D WaterTexture;
        public static Texture2D LightTexture;
        public static Texture2D EarthTexture;
        public static Texture2D BulletFireTexture;
        public static Texture2D BulletWaterTexture;
        public static Texture2D BulletLightTexture;
        public static Texture2D BulletEarthTexture;
        public static Texture2D GlowTexture;
        public static Texture2D CollectableTexture;
        public static Texture2D HealthUpTexture;
        public static Texture2D WeaponSelectTexture;
        public static Texture2D WeaponSelectSquareTexture;
        public static Texture2D EnemySelectTexture;
        public static Texture2D EnemySelectSquareTexture;
        public static Texture2D HealthFill;
        public static Texture2D PowerFill;
        public static Texture2D PowerBarGlow;
        public static Texture2D ScoreBackground;

        Texture2D Background;
        Texture2D CrosshairTexture;
        Texture2D ZeusTexture;

        public static Vector2 PlayerLocation { get; private set; }
        public static Vector2 HandLocation { get; private set; }
        public static float PlayerRotation { get; set; }

        public static readonly int GAME_WIDTH = 800;
        public static readonly int GAME_HEIGHT = 600;
        public static readonly int HUD_HEIGHT = 50;

        private const float HAND_DIST = 42f;

        private const float PLAYER_SPEED = 2f;

        private const float MAX_ROTATION = 3.14159f;
        private const float MIN_ROTATION = 0f;

        public ElementType SelectedElement { get; set; }

        private bool m_bAllowShoot = true;
        private bool m_bAllowSwitch = true;

        public static int score = 0;

        // SOUND EFFECTS
        public static SoundEffect fireExplode;
        public static SoundEffect earthExplode;
        public static SoundEffect waterExplode;
        public static SoundEffect lightningExplode;
        public static SoundEffectInstance fireExplodeInstance;
        public static SoundEffectInstance earthExplodeInstance;
        public static SoundEffectInstance waterExplodeInstance;
        public static SoundEffectInstance lightningExplodeInstance;
        public static SoundEffect powerUp;
        public static SoundEffect ZeusThrow;
        public static SoundEffectInstance ZeusThrowInstance;
        public static SoundEffect Wind;
        public static SoundEffectInstance WindInstance;

        // MUSIC
        public static SoundEffect gameIntro;
        public static SoundEffectInstance gameIntroInstance;
        public static SoundEffect gameMusic;
        public static SoundEffectInstance gameMusicInstance;
        public static bool introPlayed = false;


        // ENEMY SPAWNING
        double timer;
        double spawnTime = 2.5;
        int spawnCount = 0;
        int mouseX;
        int mouseY;
        const int numWaves = 7;

        int[] roundTime = new int[numWaves] { 23, 24, 24, 26, 26, 26, 0 };
        int[] numSpawns = new int[numWaves] { 3, 4, 5, 5, 7, 9, 12 };

        // Health/Power Level
        Texture2D mHealthBar;
        Texture2D mPowerBar;
        int mCurrentHealth = 15;
        int mPowerLevel = 0;

        SpriteFont Font1;
        Vector2 FontPos;    

         public GameScreen(ContentManager theContent, EventHandler theScreenEvent)
            : base(theScreenEvent)
        {            
            spawnTime = 2.6;
            spawnCount = 0;
            score = 0;
            Font1 = theContent.Load<SpriteFont>("Font/Font1");
            FontPos = new Vector2(GAME_WIDTH - 50, 27);
            //Load the background texture for the screen 
            PlayerRotation = (float)Math.PI / 2f;
            PlayerLocation = new Vector2(GAME_WIDTH / 2f, GAME_HEIGHT - HUD_HEIGHT);
            HandLocation = ComputeHandLocation();
            SelectedElement = (ElementType)0;
            Background = theContent.Load<Texture2D>("Main Textures/GamePlayBackground");
            ZeusTexture = theContent.Load<Texture2D>("Main Textures/ZEUS");
            CrosshairTexture = theContent.Load<Texture2D>("Main Textures/CrossHair2");
            GlowTexture = theContent.Load<Texture2D>("Main Textures/glow");

            FireTexture = theContent.Load<Texture2D>("Enemy Sprites/firesprite");
            WaterTexture = theContent.Load<Texture2D>("Enemy Sprites/watersprite");
            LightTexture = theContent.Load<Texture2D>("Enemy Sprites/lightningsprite");
            EarthTexture = theContent.Load<Texture2D>("Enemy Sprites/earthsprite");

            BulletFireTexture = theContent.Load<Texture2D>("Lightning Sprites/FireShoot2");
            BulletWaterTexture = theContent.Load<Texture2D>("Lightning Sprites/WaterShoot2");
            BulletLightTexture = theContent.Load<Texture2D>("Lightning Sprites/yellowlightning2");
            BulletEarthTexture = theContent.Load<Texture2D>("Lightning Sprites/EarthShoot2");

            CollectableTexture = theContent.Load<Texture2D>("Main Textures/PowerUp");
            HealthUpTexture = theContent.Load<Texture2D>("Main Textures/HealthUp");

            WeaponSelectTexture = theContent.Load<Texture2D>("Lightning Sprites/WeaponSelect");
            WeaponSelectSquareTexture = theContent.Load<Texture2D>("Lightning Sprites/WeaponSelectSquare");

            EnemySelectTexture = theContent.Load<Texture2D>("Lightning Sprites/EnemySelect");
            EnemySelectSquareTexture = theContent.Load<Texture2D>("Lightning Sprites/EnemySelectSquare");

            mHealthBar = theContent.Load<Texture2D>("Main Textures/PowerBar") as Texture2D;
            mPowerBar = theContent.Load<Texture2D>("Main Textures/PowerBar") as Texture2D;

            HealthFill = theContent.Load<Texture2D>("Main Textures/HealthFill") as Texture2D;
            PowerFill = theContent.Load<Texture2D>("Main Textures/PowerFill") as Texture2D;
            PowerBarGlow = theContent.Load<Texture2D>("Main Textures/PowerBarGlow");

            ScoreBackground = theContent.Load<Texture2D>("Main Textures/ScoreBackground");

            // AUDIO

            fireExplode = theContent.Load<SoundEffect>("Sounds/SOUNDFireExplosion");
            earthExplode = theContent.Load<SoundEffect>("Sounds/SOUNDEarthExplosion");
            waterExplode = theContent.Load<SoundEffect>("Sounds/SOUNDWaterExplosion");
            lightningExplode = theContent.Load<SoundEffect>("Sounds/SOUNDLightningExplosion");
            fireExplodeInstance = fireExplode.CreateInstance();
            waterExplodeInstance = waterExplode.CreateInstance();
            earthExplodeInstance = earthExplode.CreateInstance();
            lightningExplodeInstance = lightningExplode.CreateInstance();
            powerUp = theContent.Load<SoundEffect>("Sounds/SOUNDPowerUp");
            ZeusThrow = theContent.Load<SoundEffect>("Sounds/SOUNDZeusThrow");
            ZeusThrowInstance = ZeusThrow.CreateInstance();
            Wind = theContent.Load<SoundEffect>("Sounds/SOUNDWind");
            WindInstance = Wind.CreateInstance();

            gameIntro = theContent.Load<SoundEffect>("Sounds/GameSongIntro");
            gameIntroInstance = gameIntro.CreateInstance();

            gameMusic = theContent.Load<SoundEffect>("Sounds/GameSong");
            gameMusicInstance = gameMusic.CreateInstance();
        }
         static int seed = 0;
         public override void Update(GameTime theTime)
         {             
             List<Special> needSpecialDelete = new List<Special>();
             List<Projectile> needDelete = new List<Projectile>();
             List<Enemy> needDeleteE = new List<Enemy>();
             List<Collectable> needDeleteCollectable = new List<Collectable>();
             List<HealthUp> needDeleteHealthUp = new List<HealthUp>();

             if (WindInstance.State == SoundState.Stopped)
             {                 
                 WindInstance.Volume = 0.1f;
                 WindInstance.IsLooped = true;
                 WindInstance.Play();
             }

             if (gameIntroInstance.State == SoundState.Stopped && !introPlayed)
             {                 
                 gameIntroInstance.Volume = 0.75f;
                 gameIntroInstance.Play();
                 introPlayed = true;
             }

             else if (gameIntroInstance.State == SoundState.Stopped && gameMusicInstance.State == SoundState.Stopped && introPlayed)
             {
                 gameMusicInstance.IsLooped = true;
                 gameMusicInstance.Volume = 0.75f;
                 gameMusicInstance.Play();
             }
             if (mCurrentHealth <= 0)
             {
                 WindInstance.Stop();
                 gameMusicInstance.Stop();
                 gameIntroInstance.Stop();
                 ScreenEvent.Invoke(this, new EventArgs());
             }
           
             if (spawnCount == numWaves-1 && Enemy.ObjectList.Count == 0 && timer > 5 && mCurrentHealth > 0)
             {
                 WindInstance.Stop();
                 gameMusicInstance.Stop();
                 gameIntroInstance.Stop();
                 ZeusGame.outcome = true;
                 ScreenEvent.Invoke(this, new EventArgs());
             }

             KeyboardState mKeys = Keyboard.GetState();

             mCurrentHealth = (int)MathHelper.Clamp(mCurrentHealth, 0, 100);

             /*****************************
              * Spawn Enemies
              * **************************/
             if (spawnCount > 4)
             {
                 Enemy.ENEMY_SPEED = .35f;
                 spawnTime = 2.1;
             }
             else if (spawnCount > 2)
             {
                 Enemy.ENEMY_SPEED = .3f;
                 spawnTime = 2.35;
             }

             if (timer > spawnTime && numSpawns[spawnCount] > 0)
             {
                 Enemy.SpawnEnemies();
                 timer -= spawnTime;
                 numSpawns[spawnCount]--;
             }
             if (timer > roundTime[spawnCount] && spawnCount < numWaves-1)
             {
                 spawnCount++;
                 timer = 0;
             }
              
             timer += theTime.ElapsedGameTime.TotalSeconds;

             

             //check for Projectile collisions
             foreach (Projectile p in Projectile.ObjectList)
             {
                 Enemy e;

                 if (p.IsHittingEnemy(out e))
                 {
                     //projectile collided w/ something
                     needDelete.Add(p);

                     if (e != null)
                     {
                         //projectile killed this enemy
                         if (e.Location.Y > 0)
                         {
                             score += (int)e.Location.Y;
                         }
                         else if (e.Location.Y < 0)
                         {
                             score -= (int)e.Location.Y;
                         }
                         needDeleteE.Add(e);

                         Random r = new Random(seed++ * (int)DateTime.Now.Ticks);
                         int HorP = r.Next(5);

                         //Random rand = new Random();
                         //int HorP = rand.Next(5);

                        if (HorP < 4)
                        {
                            Collectable c = new Collectable(e.Location);
                            Collectable.ObjectList.Add(c);
                        }
                        else if (HorP == 4)
                        {
                            HealthUp h = new HealthUp(e.Location);
                            HealthUp.ObjectList.Add(h);
                        }
                     }
                 }
             }

             //update positions of all projectiles, check if any are off screen
             foreach (Projectile p in Projectile.ObjectList)
             {
                 p.MoveOneStep();
                 if (p.IsOffScreen())
                 {
                     needDelete.Add(p);
                 }
             }

             //check for collisions
             foreach (Special s in Special.ObjectList)
             {
                 List<Enemy> hitEnemy;
                 hitEnemy = s.GetEnemiesHit();

                 foreach (Enemy e in hitEnemy)
                 {
                    if (e.Location.Y > 0)
                    {
                        score += (int)e.Location.Y;
                    }
                    else if (e.Location.Y < 0)
                    {
                         score -= (int)e.Location.Y;
                    }
                    needDeleteE.Add(e);
                    Collectable c = new Collectable(e.Location);
                    Collectable.ObjectList.Add(c);
                 }
             }

             //update positions of all Special, check if any are off screen
             foreach (Special s in Special.ObjectList)
             {
                 s.MoveOneStep();
                 if (s.IsOffScreen())
                 {
                     needSpecialDelete.Add(s);
                 }
             }

             foreach (Enemy e in Enemy.ObjectList)
             {
                 e.MoveOneStep();
                 if (e.IsHittingBottom())
                 {
                     mCurrentHealth--;
                     needDeleteE.Add(e);
                 }
             }

             foreach (Collectable co in Collectable.ObjectList)
             {
                 co.MoveOneStep();

                 if (co.IsHittingPlayer())
                 {
                     needDeleteCollectable.Add(co);
                     //TODO: powerup zeus here
                     if (mPowerLevel < 10)
                     {
                         mPowerLevel++;
                     }
                 }
                 else if (co.IsHittingBottom())
                 {
                     needDeleteCollectable.Add(co);
                 }
             }

             foreach (HealthUp hu in HealthUp.ObjectList)
             {
                 hu.MoveOneStep();

                 if (hu.IsHittingPlayer())
                 {
                     needDeleteHealthUp.Add(hu);
                     //TODO: powerup zeus here
                     if (mCurrentHealth < 15)
                     {
                         mCurrentHealth++;
                     }
                 }
                 else if (hu.IsHittingBottom())
                 {
                     needDeleteHealthUp.Add(hu);
                 }
             }

             //delete the offscreen bullets
             foreach (Projectile p in needDelete)
             {
                 p.Delete();
             }

             foreach (Special s in needSpecialDelete)
             {
                 s.Delete();
             }

             foreach (Enemy e in needDeleteE)
             {
                 e.Delete();
             }

             foreach (var item in needDeleteCollectable)
             {
                 item.Delete();
             }

             foreach (var item in needDeleteHealthUp)
             {
                 item.Delete();
             }

             //get player rotation from mouse pos
             PlayerRotation = GetRotationToMousePos(PlayerLocation);
             HandLocation = ComputeHandLocation();

             float fRotationAlongFlight = GetRotationToMousePos(HandLocation);

             CheckToShoot(fRotationAlongFlight);

             //switch selected element
             CheckToSwitchElement();

             //move the player
             if (Keyboard.GetState().IsKeyDown(Keys.A))
             {
                 MovePlayer(true);
             }
             else if (Keyboard.GetState().IsKeyDown(Keys.D))
             {
                 MovePlayer(false);
             }

             //recompute hand loc, in case player has moved
             HandLocation = ComputeHandLocation();

             base.Update(theTime);
         }
         private void CheckToShoot(float shootAngle)
         {
             if (Mouse.GetState().LeftButton == ButtonState.Pressed)
             {
                 if (m_bAllowShoot)
                 {
                     //create and add a new projectile
                     Projectile newBullet = new Projectile(SelectedElement, shootAngle);
                     //newBullet.SetToEndOfCannon(ZeusTexture.Height / 2);
                     Projectile.ObjectList.Add(newBullet);
                     //don't allow holding down ctrl key
                     m_bAllowShoot = false;

                     //change element
                     Random r = new Random(DateTime.Now.Millisecond);

                 }
             }
             else if (Mouse.GetState().RightButton == ButtonState.Pressed && mPowerLevel == 10)
             {
                 if (m_bAllowShoot)
                 {
                     //create and add a new projectile
                     Special.SpawnWave(SelectedElement);
                     //don't allow holding down ctrl key
                     m_bAllowShoot = false;
                     mPowerLevel = 0;
                 }
             }
             else
             {
                 m_bAllowShoot = true;
             }
         }

         private void CheckToSwitchElement()
         {
             if (Keyboard.GetState().IsKeyDown(Keys.W))
             {
                 if (m_bAllowSwitch)
                 {
                     SelectedElement = (ElementType)(((int)SelectedElement + 1) % 4);
                     m_bAllowSwitch = false;
                 }
             }
             else if (Keyboard.GetState().IsKeyDown(Keys.S))
             {
                 if (m_bAllowSwitch)
                 {
                     SelectedElement = (ElementType)(((int)SelectedElement + 3) % 4);
                     m_bAllowSwitch = false;
                 }
             }
             else
             {
                 m_bAllowSwitch = true;
             }
         }
         private void DrawCrosshair(SpriteBatch batch)
         {
             MouseState mstate = Mouse.GetState();
             mouseX = mstate.X;
             mouseY = mstate.Y;
             if (mouseX < 100)
                 mouseX = 100;
             else if (mouseX > GAME_WIDTH - 100)
                 mouseX = GAME_WIDTH - 100;
             if (mouseY < 0)
                 mouseY = 0;
             else if (mouseY > GAME_HEIGHT - 50)
                 mouseY = GAME_HEIGHT - 50;              
             
             Vector2 mouseLoc = new Vector2(mouseX - (CrosshairTexture.Width / 2),
                                                mouseY - (CrosshairTexture.Height / 2));
             
             batch.Draw(CrosshairTexture, mouseLoc, Color.Black);
         }

         private void DrawZeus(SpriteBatch batch)
         {
             Vector2 origin = new Vector2(ZeusTexture.Width / 2f, ZeusTexture.Height / 2f);
             batch.Draw(ZeusTexture,
                 new Rectangle((int)PlayerLocation.X /*- (ZeusTexture.Width / 2)*/,
                               (int)PlayerLocation.Y /*- (ZeusTexture.Height / 2)*/,
                               ZeusTexture.Width, ZeusTexture.Height),
                 null, Color.White, MathHelper.PiOver2 - PlayerRotation, origin, SpriteEffects.None,
                 0);
         }

         public void DrawWeaponSelect(SpriteBatch batch)
         {
             Vector2 location = new Vector2(0,45);
             batch.Draw(WeaponSelectTexture, location, Color.White);
         }

         public void DrawEnemySelect(SpriteBatch batch)
         {
             Vector2 location = new Vector2(700, 45);
             batch.Draw(EnemySelectTexture, location, Color.White);
         }

         public void DrawPowerBarGlow(SpriteBatch batch)
         {
             Vector2 location = new Vector2(710, 335);
             if (mPowerLevel == 10)
             {
                 batch.Draw(PowerBarGlow, location, Color.White);
             }
         }

         private void DrawGlow(SpriteBatch batch)
         {
             Color color;
             Vector2 weaponLocation = new Vector2(0, 0);
             Vector2 enemyLocation = new Vector2(0, 0);

             switch (SelectedElement)
             {
                 case ElementType.Fire:
                     color = Color.OrangeRed;
                     weaponLocation.X = 20;
                     weaponLocation.Y = 90;
                     enemyLocation.X = GAME_WIDTH - 80;
                     enemyLocation.Y = 90;
                     break;
                 case ElementType.Earth:
                     color = Color.Green;
                     weaponLocation.X = 20;
                     weaponLocation.Y = 240;
                     enemyLocation.X = GAME_WIDTH - 80;
                     enemyLocation.Y = 240;
                     break;
                 case ElementType.Water:
                     color = Color.Blue;
                     weaponLocation.X = 20;
                     weaponLocation.Y = 190;
                     enemyLocation.X = GAME_WIDTH - 80;
                     enemyLocation.Y = 190;
                     break;
                 default:
                     color = Color.Yellow;
                     weaponLocation.X = 20;
                     weaponLocation.Y = 140;
                     enemyLocation.X = GAME_WIDTH - 80;
                     enemyLocation.Y = 140;

                     break;
             }

             //it wants the top left corner for location, not the center
             Vector2 drawLoc = new Vector2(HandLocation.X - (GlowTexture.Width / 2),
                                           HandLocation.Y - (GlowTexture.Height / 2));
             batch.Draw(GlowTexture, HandLocation, null, color, MathHelper.PiOver2 - PlayerRotation, (HandLocation - drawLoc), 1f, SpriteEffects.None, 0);
             batch.Draw(WeaponSelectSquareTexture,weaponLocation,Color.White);
             batch.Draw(EnemySelectSquareTexture, enemyLocation, Color.White);
         }

         private float GetRotationToMousePos(Vector2 startLoc)
         {
             MouseState mstate = Mouse.GetState();

             float adjacent = MathHelper.Distance(mstate.X, startLoc.X);
             float opposite = MathHelper.Distance(mstate.Y, startLoc.Y);

             float angle = (float)Math.Atan(opposite / adjacent);

             if (mstate.X < startLoc.X)
             {
                 angle = (float)Math.PI - angle;
             }

             //if mouse is out of bounds, set to 
             if (mstate.Y > startLoc.Y)
             {
                 if (mstate.X > startLoc.X)
                 {
                     angle = MIN_ROTATION;
                 }
                 else
                 {
                     angle = MAX_ROTATION;
                 }
             }

             //make sure angle is within range
             angle = MathHelper.Clamp(angle, MIN_ROTATION, MAX_ROTATION);

             return angle;
         }

         private Vector2 ComputeHandLocation()
         {
             float cos = (float)Math.Cos(MathHelper.PiOver4 - PlayerRotation);
             float sin = (float)Math.Sin(MathHelper.PiOver4 - PlayerRotation);

             float xDist = cos * HAND_DIST;
             float yDist = sin * HAND_DIST;



             return new Vector2(PlayerLocation.X + xDist, PlayerLocation.Y + yDist);
         }

         private void MovePlayer(bool bMoveLeft)
         {
             float newX = PLAYER_SPEED;

             if (bMoveLeft)
             {
                 newX = PlayerLocation.X - newX;
             }
             else
             {
                 newX = PlayerLocation.X + newX;
             }

             float width = ZeusTexture.Width / 2f;
             newX = MathHelper.Clamp(newX, width, GAME_WIDTH - width);

             PlayerLocation = new Vector2(newX, PlayerLocation.Y);
        }
        public override void Draw(SpriteBatch theBatch)
        {

            theBatch.Draw(Background, new Rectangle(0, 0, GAME_WIDTH, GAME_HEIGHT), Color.White);
            
            Vector2 FontOrigin = Font1.MeasureString(score.ToString()) / 2;
            Vector2 ScoreBackgroundPos = new Vector2(GAME_WIDTH - 92, 10);
            
            theBatch.Draw(ScoreBackground, ScoreBackgroundPos , Color.White);
            theBatch.DrawString(Font1, score.ToString(), FontPos, Color.Maroon,
            0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);

            foreach (Projectile p in Projectile.ObjectList)
            {
                p.Draw(theBatch);
            }
            foreach (Special s in Special.ObjectList)
            {
                s.Draw(theBatch);
            }

            foreach (Enemy e in Enemy.ObjectList)
            {
                e.Draw(theBatch);
            }

            foreach (Collectable co in Collectable.ObjectList)
            {
                co.Draw(theBatch);
            }

            foreach (HealthUp hu in HealthUp.ObjectList)
            {
                hu.Draw(theBatch);
            }
            //draw crosshair
            DrawCrosshair(theBatch);            
            //draw weapons
            DrawWeaponSelect(theBatch);
            //draw enemy selector
            DrawEnemySelect(theBatch);
            //draw glow
            DrawGlow(theBatch);
            //draw zeus
            DrawZeus(theBatch);
            

            
            // Draw the Health Bar container and the Health Fill based on mCurrentHealth         
            theBatch.Draw(mHealthBar, new Rectangle(32, 350, mHealthBar.Width, mHealthBar.Height), Color.White);
            theBatch.Draw(HealthFill, new Rectangle(39, 356+(195/15*(15-mCurrentHealth)), HealthFill.Width, (HealthFill.Height*mCurrentHealth/15)), Color.White);
                        
            // Draw the Power Bar container and the Power Fill based on mPowerLevel
            theBatch.Draw(mPowerBar, new Rectangle(732, 350, mPowerBar.Width, mPowerBar.Height), Color.White);
            DrawPowerBarGlow(theBatch);
            theBatch.Draw(PowerFill, new Rectangle(739, 546-(19*mPowerLevel), PowerFill.Width, (PowerFill.Height*mPowerLevel/10)), Color.White);

            
            base.Draw(theBatch);
        }
    }
}
